using System.Buffers;
using System.Buffers.Binary;
using System.Text;
using SuperSocket.ProtoBase;

namespace SuperSocketProxy;

public static class SequenceReaderExtensions
{
    private static bool TryReadReverseEndianness(ref SequenceReader<byte> reader, out byte value)
    {
        if (!reader.TryRead(out value))
            return false;
        
        value = BinaryPrimitives.ReverseEndianness(value);
        
        return true;
    }
    
    public static ReadOnlySpan<byte> Read(this ref SequenceReader<byte> reader,int offset, int length)
    {
        if (reader.Length < length)
            throw new Exception();

        try
        {
            return reader.Sequence.Slice(offset, length).ToArray();
        }
        finally
        {
            reader.Advance(length);
        }
    }
    
    public static string ReadStringLengthEncodedWithBit(this ref SequenceReader<byte> reader)
    {
        reader.TryRead(out byte length);
        
        if (length == 0)
            return string.Empty;

        return reader.ReadString(length, Encoding.UTF8);
    }
        
    public static string ReadStringLittleEndianWithBit(this ref SequenceReader<byte> reader)
    {
        TryReadReverseEndianness(ref reader, out byte length);
        
        if (length == 0)
            return string.Empty;

        return reader.ReadString(length, Encoding.UTF8);
    }
    
    public static string ReadStringLengthEncodedWithInt16(this ref SequenceReader<byte> reader)
    {
        reader.TryReadBigEndian(out short length);

        if (length == 0)
            return string.Empty;

        return reader.ReadString(length, Encoding.UTF8);
    }
        
    public static string ReadStringLittleEndianWithInt16(this ref SequenceReader<byte> reader)
    {
        reader.TryReadLittleEndian(out short length);

        if (length == 0)
            return string.Empty;

        return reader.ReadString(length, Encoding.UTF8);
    }
    
    public static string ReadMiddleString(this ref SequenceReader<byte> reader, ReadOnlySpan<byte> beginMark, ReadOnlySpan<byte> endMark)
    {
        if (!reader.TryReadTo(out ReadOnlySequence<byte> _, beginMark, advancePastDelimiter: true))
            return null;
                
        if (!reader.TryReadTo(out ReadOnlySequence<byte> pack, endMark, advancePastDelimiter: true))
            return null;

        return pack.GetString(Encoding.UTF8);
    }
    
    public static ReadOnlySequence<byte> ReadMiddleSequence(this ref SequenceReader<byte> reader, ReadOnlySpan<byte> beginMark, ReadOnlySpan<byte> endMark)
    {
        if (!reader.TryReadTo(out ReadOnlySequence<byte> _, beginMark, advancePastDelimiter: true))
            return default;
                
        if (!reader.TryReadTo(out ReadOnlySequence<byte> pack, endMark, advancePastDelimiter: true))
            return default;

        return pack;
    }
    
    public static ReadOnlySequence<byte> GetLeftSequence(this ref SequenceReader<byte> reader, ReadOnlySpan<byte> endMark)
    {
        if (!reader.TryReadTo(out ReadOnlySequence<byte> pack, endMark, advancePastDelimiter: false))
            return default;
                
        return pack;
    }
    
    public static ReadOnlySequence<byte> GetRightSequence(this ref SequenceReader<byte> reader, ReadOnlySpan<byte> beginMark)
    {
        if (!reader.IsNext(beginMark, advancePast: true))
            return default;
        
        return reader.Sequence;
    }
    
    public static string GetRightString(this ref SequenceReader<byte> reader, ReadOnlySpan<byte> beginMark)
    {
        if (!reader.IsNext(beginMark, advancePast: true))
            return default;

        return reader.Sequence.GetString(Encoding.UTF8);
    }
    
    public static string ReadString(this ref SequenceReader<byte> reader, int length, Encoding encoding)
    {
        var buffer = reader.Sequence.Slice(reader.Consumed, length);

        try
        {
            return buffer.GetString(encoding);
        }
        finally
        {
            reader.Advance(length);
        }
    }
}